
package tomkitty.turdlet;

import tomkitty.server.*;
import tomkitty.util.*;
import java.io.*;
import java.net.*;
import java.util.*;

/**
 * Creates Turdlets for you.
 *
 * @author micha
 */
public class TurdletFactory implements HttpConstants {

	/**
	 * Runs a turdlet and waits for it to be done.
	 *
	 * @param t The turdlet to run
	 */
	public void runTurdlet(Turdlet t) {
		t.doit();

		while (! t.isDone()) {
			// spin waiting for the turdlet to be finished
		}
	}
		
	/**
	 * Creates a new {@link ErrorTurdlet} and initializes it with the
	 * {@link TurdletContext} and an HTTP status code string.
	 *
	 * @param ctx The turdlet's context object
	 * @param status The HTTP status code string
	 * @return A newly initialized {@link Turdlet}
	 */
	public Turdlet errorTurdlet(TurdletContext ctx,
			String status) {

		ErrorTurdlet et = new ErrorTurdlet();
		et.init(ctx);

		et.setErr(status);
		et.setErrNo(status);

		return (Turdlet) et;
	}

	public Turdlet RedirectError(TurdletContext ctx, 
			String location) {
		URI uri = ctx.getRequest().getUri();
		Turdlet t = errorTurdlet(ctx, HTTP_MOVED_PERM);
		t.header("Location: " + uri.getScheme() + "://" + uri.getAuthority() 
				+ location);
		return t;
	}

	public Turdlet ForbiddenError(TurdletContext ctx) {
		return errorTurdlet(ctx, HTTP_FORBIDDEN);
	}

	public Turdlet NotFoundError(TurdletContext ctx) {
		return errorTurdlet(ctx, HTTP_NOT_FOUND);
	}

	public Turdlet InternalError(TurdletContext ctx) {
		return errorTurdlet(ctx, HTTP_INTERNAL_ERROR);
	}

	public Turdlet ServerError(TurdletContext ctx) {
		return errorTurdlet(ctx, HTTP_SERVER_ERROR);
	}

	public Turdlet BadRequestError(TurdletContext ctx) {
		return errorTurdlet(ctx, HTTP_BAD_REQUEST);
	}

	public Turdlet create(TurdletContext ctx) {
		
		HttpRequest req = null;
		Turdlet theTurd = null;
		BufferedReader br = ctx.getIn();
		String firstLine;
		String fields;

		try {
			// grab header
			if ( (firstLine = br.readLine()) == null) {
				return BadRequestError(ctx);
			}

			// grab the header fields
			fields = "";
			for(int i = 0; br.ready() && i < 3;) {
				String line = br.readLine();
				if (line.length() == 0) {
					i++;
				} else {
					i = 0;
					fields = fields + line + "\n";
				}
			}

		} catch (IOException e) {
			e.printStackTrace();
			return ServerError(ctx);
		}

		try {

			Debug.d("===== REQUEST START");
			Debug.d(firstLine);
			Debug.d(fields);
			Debug.d("");
			Debug.d("===== REQUEST END");

			req = new HttpRequest(firstLine, fields);
			ctx.setRequest(req);

			if (! req.isValid()) {
				return BadRequestError(ctx);
			} else if (req.getMethod().equals("HEAD")) {
				ctx.getOut().sendHeadersOnly(true);
			} else if (! req.getMethod().equals("GET")) {
				return InternalError(ctx);
			} 
			
		} catch (Exception e) {

			// getting NullPointerExceptions in the regex class for some reason.
			// i suspect it's because Matcher isn't a thread-safe class

			Debug.e("unexpected error parsing request headers");
			e.printStackTrace();
			return ServerError(ctx);

		}

		// headers are ok and resourceName was set

		try {

			// create an instance of the turdlet by name. if no such turdlet exists 
			// there will be a ClassNotFoundException caught.

			theTurd = AppLoader.getTurdlet(req.getUri().getPath());
			theTurd.init(ctx);

		} catch (ClassNotFoundException e) {
						
			// class not found
			// we load a turdlet here that tries to interpret the
			// uri path as a static html file to regurgitate. if
			// the file doesn't exist then the static loader turdlet
			// will delegate to the not found error turdlet.

			theTurd = new StaticTurdlet();
			theTurd.init(ctx);

		} catch (Exception e) {

			// some other problem instantiating the turdlet. maybe the
			// app turdlet is messed up in some way or something.

			e.printStackTrace();
			theTurd = ServerError(ctx);
		}

		return theTurd;

	}

}

